{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Scoring grid search with metric: f1_micro\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import visual_bow as bow\n",
    "from sklearn.cluster import MiniBatchKMeans\n",
    "from sklearn.svm import SVC\n",
    "from sklearn.grid_search import GridSearchCV\n",
    "from sklearn.ensemble import AdaBoostClassifier\n",
    "from sklearn.externals import joblib\n",
    "import glob\n",
    "import random\n",
    "import warnings\n",
    "\n",
    "SCORING = 'f1_micro'\n",
    "print 'Scoring grid search with metric: %s' % SCORING"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "9106 total negative imgs to choose from\n",
      "[('101_ObjectCategories/rooster/image_0014.jpg', False), ('101_ObjectCategories/rooster/image_0023.jpg', False), ('101_ObjectCategories/rooster/image_0040.jpg', False), ('101_ObjectCategories/rooster/image_0013.jpg', False), ('101_ObjectCategories/rooster/image_0038.jpg', False)]\n"
     ]
    }
   ],
   "source": [
    "# Get all possible negative images and label them False\n",
    "positive_folder='panda'\n",
    "all_negs = [(path, False) for path in bow.neg_img_cal101(positive_folder)]\n",
    "print '%i total negative imgs to choose from' % len(all_negs)\n",
    "print all_negs[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "674 positive images\n",
      "[('panda_rip/image_0014.jpg', True), ('panda_rip/75.JPEG', True), ('panda_rip/345.JPEG', True), ('panda_rip/30.JPEG', True), ('panda_rip/106.JPEG', True)]\n"
     ]
    }
   ],
   "source": [
    "# Get all the positive images you have (in the panda_rip folder) and label them True\n",
    "positive_imgs = [(path, True) for path in glob.glob('panda_rip/*')]\n",
    "print '%i positive images' % len(positive_imgs)\n",
    "print positive_imgs[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1348 total images (1:1 positive:negative)\n",
      "[('101_ObjectCategories/gerenuk/image_0017.jpg', False), ('101_ObjectCategories/Faces/image_0392.jpg', False), ('101_ObjectCategories/watch/image_0014.jpg', False), ('panda_rip/72.JPEG', True), ('panda_rip/184.JPEG', True)]\n"
     ]
    }
   ],
   "source": [
    "# take N random negative images, where N is no of positive images\n",
    "# then concatenate N pos + N neg and shuffle.\n",
    "chosen_negs = random.sample(all_negs, len(positive_imgs))\n",
    "imgs = chosen_negs + positive_imgs\n",
    "\n",
    "np.random.shuffle(imgs)\n",
    "\n",
    "print '%i total images (1:1 positive:negative)' % len(imgs)\n",
    "print imgs[:5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "img_descs, y = bow.gen_sift_features(imgs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['pickles/img_descs/y.pickle', 'pickles/img_descs/y.pickle_01.npy']"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# joblib.dump(img_descs, 'pickles/img_descs/img_descs.pickle')\n",
    "# joblib.dump(y, 'pickles/img_descs/y.pickle')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train-test-val split: 944 training rows, 202 test rows, 202 validation rows\n"
     ]
    }
   ],
   "source": [
    "# generate indexes for train/test/val split\n",
    "training_idxs, test_idxs, val_idxs = bow.train_test_val_split_idxs(\n",
    "    total_rows=len(imgs), \n",
    "    percent_test=0.15, \n",
    "    percent_val=0.15\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Cluster the SIFT descriptors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "%%time\n",
    "\n",
    "K_CLUSTERS = 250\n",
    "\n",
    "# MiniBatchKMeans annoyingly throws tons of deprecation warnings that fill up the notebook. Ignore them.\n",
    "warnings.filterwarnings('ignore')\n",
    "\n",
    "X, cluster_model = bow.cluster_features(\n",
    "    img_descs, \n",
    "    training_idxs=training_idxs, \n",
    "    cluster_model=MiniBatchKMeans(n_clusters=K_CLUSTERS)\n",
    ")\n",
    "\n",
    "warnings.filterwarnings('default')\n",
    "\n",
    "X_train, X_test, X_val, y_train, y_test, y_val = bow.perform_data_split(X, y, training_idxs, test_idxs, val_idxs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Uncomment to pickle the clustered Visual BoW features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# for obj, obj_name in zip( [X_train, X_test, X_val, y_train, y_test, y_val], \n",
    "#                          ['X_train', 'X_test', 'X_val', 'y_train', 'y_test', 'y_val'] ):\n",
    "#     joblib.dump(obj, 'pickles/feature_data/%s.pickle' % obj_name)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Uncomment to LOAD pickle of clustered Visual BoW features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# for obj_name in ['X_train', 'X_test', 'X_val', 'y_train', 'y_test', 'y_val']:\n",
    "#     exec(\"{obj_name} = joblib.load('pickles/feature_data/{obj_name}.pickle')\".format(obj_name=obj_name))\n",
    "#     exec(\"print obj_name, len({0})\".format(obj_name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Classify with SVM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train score (f1_micro): 0.91313559322\n",
      "test score (f1_micro): 0.886138613861\n",
      "SVC(C=5, cache_size=200, class_weight=None, coef0=0.0,\n",
      "  decision_function_shape=None, degree=3, gamma=0.0001, kernel='rbf',\n",
      "  max_iter=-1, probability=False, random_state=None, shrinking=True,\n",
      "  tol=0.001, verbose=False)\n",
      "CPU times: user 1.66 s, sys: 109 ms, total: 1.77 s\n",
      "Wall time: 14 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "# c_vals = [0.0001, 0.01, 0.1, 1, 10, 100, 1000]\n",
    "c_vals = [0.1, 1, 5, 10]\n",
    "# c_vals = [1]\n",
    "\n",
    "gamma_vals = [0.5, 0.1, 0.01, 0.0001, 0.00001]\n",
    "# gamma_vals = [0.5, 0.1]\n",
    "# gamma_vals = [0.1]\n",
    "\n",
    "param_grid = [\n",
    "  {'C': c_vals, 'kernel': ['linear']},\n",
    "  {'C': c_vals, 'gamma': gamma_vals, 'kernel': ['rbf']},\n",
    " ]\n",
    "\n",
    "svc = GridSearchCV(SVC(), param_grid, n_jobs=-1, scoring=SCORING)\n",
    "svc.fit(X_train, y_train)\n",
    "print 'train score (%s):'%SCORING, svc.score(X_train, y_train)\n",
    "print 'test score (%s):'%SCORING, svc.score(X_test, y_test)\n",
    "\n",
    "print svc.best_estimator_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# We have our estimator, this is how it could classify random pictures"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "101_ObjectCategories/BACKGROUND_Google/image_0460.jpg ['False']\n",
      "101_ObjectCategories/minaret/image_0032.jpg ['False']\n",
      "101_ObjectCategories/Motorbikes/image_0370.jpg ['False']\n",
      "101_ObjectCategories/pagoda/image_0047.jpg ['False']\n",
      "101_ObjectCategories/sunflower/image_0062.jpg ['False']\n",
      "101_ObjectCategories/bonsai/image_0066.jpg ['True']\n",
      "101_ObjectCategories/wheelchair/image_0027.jpg ['False']\n",
      "101_ObjectCategories/Motorbikes/image_0776.jpg ['True']\n",
      "101_ObjectCategories/Faces/image_0195.jpg ['False']\n",
      "101_ObjectCategories/trilobite/image_0054.jpg ['False']\n"
     ]
    }
   ],
   "source": [
    "for img_path, label in random.sample(all_negs, 10):\n",
    "    print img_path, svc.predict(bow.img_to_vect(img_path, cluster_model))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Uncomment to pickle the best SVC classifier & kmeans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# joblib.dump(svc.best_estimator_, 'pickles/svc/svc.pickle')\n",
    "# joblib.dump(cluster_model, 'pickles/cluster_model/cluster_model.pickle')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Try AdaBoost, it's a common choice for SIFT features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "train score (f1_micro): 1.0\n",
      "test score (f1_micro): 0.816831683168\n",
      "AdaBoostClassifier(algorithm='SAMME.R', base_estimator=None,\n",
      "          learning_rate=1.1, n_estimators=250, random_state=None)\n",
      "CPU times: user 4.09 s, sys: 97 ms, total: 4.19 s\n",
      "Wall time: 1min 44s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "\n",
    "ada_params = {\n",
    "    'n_estimators':[100, 250, 500, 750],\n",
    "    'learning_rate':[0.8, 0.9, 1.0, 1.1, 1.2]\n",
    "}\n",
    "\n",
    "# ada = AdaBoostClassifier(n_estimators=MAX_ESTIMATORS, learning_rate=0.8)\n",
    "ada = GridSearchCV(AdaBoostClassifier(), ada_params, n_jobs=-1, scoring=SCORING)\n",
    "ada.fit(X_train, y_train)\n",
    "print 'train score (%s):'%SCORING, ada.score(X_train, y_train)\n",
    "print 'test score (%s):'%SCORING, ada.score(X_test, y_test)\n",
    "print ada.best_estimator_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Uncomment to pickle the AdaBoostClassifier"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "# joblib.dump(ada.best_estimator_, 'pickles/ada/ada.pickle');\n",
    "# print 'picked adaboost'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TODO\n",
    "\n",
    "* Separate out the clustering from the feature generation. They should be 2 different functions, the clustering should take the SIFT **training** data as an argument. It has labels already, right? Then you can save the SIFT data before clustering. Finally, you can do a grid search across K_CLUSTERS.\n",
    "\n",
    "* Also it would be cool to graph the above."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
